let formCalls = [];
let form;

/**
 * layui的form组件的React封装
 *
 * 封装了form初始化与渲染
 *
 * props:
 * type = pane , pane样式的表单,不传则默认为普通的form样式
 * verify = func,  返回layui verify对象的接口, 就是执行所有自定义校验的
 * size = "sm" 目前只有sm可选,不传为缺省大小，设置为sm则所有表单中label, input ,select都会小一号
 *
 * 表单提交按钮样式:
 * 表单提交按钮新增了新的样式layui-form-btn, 按钮必须放在Item组件中
 *
 * ex:
 * <Form type="pane" verify={()=>{xxx}} size="sm">
 *     <Item>
 *         <Label>xxx</Label>
 *         <Input/>
 *     </Item>
 *     <Item>
 *         <Label>yyy</Label>
 *         <Select>...</Select>
 *     </Item>
 * </Form>
 */
class Form extends React.Component {

    componentDidMount() {
        layui.use(['form'], () => {
            form = layui.form;
            for (let i = 0; i < formCalls.length; i++) {
                for (let key in formCalls[i]) {
                    form[key](...formCalls[i][key]);
                }
            }
            formCalls = [];
            if (this.props.verify) {
                form.verify(this.props.verify());
            }
            this.setState({}, () => {
                layui.form.render();
            });
        });
    }

    componentDidUpdate() {
        if (this.props.onUpdateComplete)
            this.props.onUpdateComplete();

        if (form)
            form.render();
    }

    render() {
        let defClass = "layui-form ";
        let frameClass = "";
        let {className, style, frameStyle, type, size, ...params} = this.props;
        if (type == "pane") {
            defClass += "layui-form-pane ";
        }
        if (size == 'sm') {
            frameClass += "layui-form-sm ";
        }

        return (
            <div className={frameClass} style={frameStyle}>
                <form className={defClass} style={style} {...params}>
                    {this.props.children}
                </form>
            </div>
        )
    }
}

function guid() {
    function S4() {
        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
    }

    return ('a' + S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
}

/**
 * 封装layui 表单内行控件
 *
 * @block : true则表示正行显示，也可包含radio
 */
class Item extends React.Component {
    render() {
        let {className, ...other} = this.props;
        className = className ? className : 'layui-form-item';

        if (this.props.block)
            className += " layui-input-block"
        return (
            <div className={className} {...other}>
                {this.props.children}
            </div>
        )
    }
}

class ItemBlock extends React.Component {
    render() {

        return (
            <div className="layui-input-block">
                {this.props.children}
            </div>
        )
    }
}

/**
 * 封装layui 文本域
 */
class TextItem extends React.Component {
    render() {
        return (
            <div className={'layui-form-item layui-form-text'} {...this.props}>
                {this.props.children}
            </div>
        )
    }
}

/**
 * 表单内一行左边的标题
 *
 * props:
 * width - hg,lg 超大,大 , 不填是正常
 */
class Label extends React.Component {

    render() {
        let width = this.props.width;
        let style = {};
        if (width == 'hg')
            style = {width: 150};
        else if (width == 'lg')
            style = {width: 130};
        return (
            <label className="layui-form-label" style={style}>{this.props.children}</label>
        )
    }
}

/**
 * 表单内的输入框
 *
 * 除了样式外,属性可透传
 *
 * @props
 *
 * width : sm, xs
 * size : sm
 * @block : true 表示占整行
 */
class Input extends React.Component {
    constructor(props) {
        super(props);
        this.state = {readonly: this.props.readonly};
        this.sizeClass = this.props.size === 'sm' ? 'layui-input-small' : '';
    }

    set value(value) {
        this.refs.input.value = value;
    }

    get value() {
        return this.refs.input.value;
    }

    onChange(value) {
        if (this.props.onChange)
            this.props.onChange(value);
    }

    render() {
        let {className, ref, readOnly, block, onChange, frameStyle, ...other} = this.props;
        frameStyle = frameStyle ? frameStyle : {};
        if (this.props.width === 'sm' && block !== true)
            frameStyle.width = '100px';
        else if (this.props.width === 'xs' && block !== true)
            frameStyle.width = '70px';

        return (
            <div className={block === true ? "layui-input-block" : "layui-input-inline"} style={frameStyle}>
                <input {...other} readOnly={this.state.readonly ? this.state.readonly : readOnly} className={"layui-input " + this.sizeClass} ref="input"
                       style={this.props.style} onChange={(e) => this.onChange(e.target.value)}/>
            </div>
        )
    }
}

/**
 * layui Select控件封装
 *
 * prop
 * @size : sm 可以调整高度
 * 包括初始化,事件绑定,自渲染,选中项的值和文本的获取接口
 */
class Select extends React.Component {
    constructor(props) {
        super(props);
        this.layuiFilter = guid();
        this.className = "layui-input-inline";
        if (this.props.size == "sm")
            this.className += " layui-select-sm";

        this.level = 0;
    }

    get value() {
        return this.refs[this.layuiFilter].value;
    }

    set value(value) {
        this.refs[this.layuiFilter].value = value;
        if (form) form.render('select');
    }

    get text() {
        let index = this.refs[this.layuiFilter].selectedIndex;
        return this.refs[this.layuiFilter][index].label;
    }

    onChange(data) {
        if (this.props.onChange) {
            this.props.onChange(data.value);
            form.render('select');
        }
    }

    /**
     * 可以在select中将数据构造成一个树形结构的列表，用---来代表层级关系
     * @param list           数据源
     * @param parent         上层属性名，调用时，永远是null
     * @param parentIdName   parent所在属性的名字
     * @param idName         当前属性的id所在名字
     * @param nameProperty   用于显示的属性所在名字
     * @returns {Array}      返回构造好的数据
     */
    getTreeData(list, parent, parentIdName, idName, nameProperty) {
        this.level += 1;
        let ret = [];
        for (let i = 0; i < list.length; i++) {
            if (list[i][parentIdName] == "")
                list[i][parentIdName] = null;

            if (list[i][parentIdName] == parent) {
                let gap = "";
                for (let j = 1; j < this.level; j++) {
                    gap += "---"
                }

                list[i][nameProperty] = gap + list[i][nameProperty];
                ret.push(list[i]);
                let subNodes = this.getTreeData(list, list[i][idName], parentIdName, idName, nameProperty);
                if (subNodes.length > 0)
                    ret.push(...subNodes);
            }
        }
        this.level -= 1;
        return ret;
    }

    componentDidMount() {
        if (this.props.onChange) {
            if (form) {
                form.on('select(' + this.layuiFilter + ')', (data) => this.onChange(data));
            }
            else {
                let on = {"on": ['select(' + this.layuiFilter + ')', (data) => this.onChange(data)]};
                formCalls.push(on);
            }
        }

        if (form)
            form.render('select');
    }

    componentDidUpdate() {
        if (form)
            form.render('select');
    }

    render() {
        return (
            <div className={this.className} style={this.props.style}>
                <select {...this.props} lay-filter={this.layuiFilter} ref={this.layuiFilter}>
                    {this.props.children}
                </select>
            </div>
        )
    }
}

/**
 * 表单中文本区域控件封装
 */
class Textarea extends React.Component {
    get value() {
        return this.refs.area.value;
    }

    set value(value) {
        this.refs.area.value = value ? value : '';
    }

    onChange(value) {
        if (this.props.onChange)
            this.props.onChange(value);
    }

    render() {
        let {className, onChange, ...other} = this.props;
        return (
            <div className="layui-input-block">
                <textarea className="layui-textarea" {...other} ref="area"
                          onChange={(e) => this.onChange(e.target.value)}/>
            </div>
        )
    }
}

/**
 * layui checkbox控件封装
 *
 * 包括初始化,事件绑定,自渲染
 *
 * onClick(checked) :
 *
 * props:
 * @lay-skin primary 设置成原生风格的复选框,否则是个很大的框
 @width  可以是sm,xs, 会缩小外面的margin和padding
 * 点击回调,会将是否选中作为参数传回
 */
class Checkbox extends React.Component {

    constructor(props) {
        super(props);
        this.layuiFilter = guid();
        this.stateChecked = this.props.checked;
    }

    componentDidMount() {

        if (form) {
            form.on('checkbox(' + this.layuiFilter + ')', (data) => this.onClick(data));
            form.render('checkbox');
        }
        else {
            let on = {"on": ['checkbox(' + this.layuiFilter + ')', (data) => this.onClick(data)]};
            formCalls.push(on);
        }

    }

    get checked() {
        return this.stateChecked;
    }

    set checked(value) {
        this.stateChecked = value;
        this.refs.thisbox.checked = this.stateChecked;
        if (form) {
            form.render('checkbox');
        }
    }

    onClick(data) {
        this.stateChecked = data.elem.checked;
        if (this.props.onClick) {
            this.props.onClick(this.stateChecked);
            form.render('checkbox');
        }
    }

    render() {
        let {type, frameStyle, ref, ...others} = this.props;
        frameStyle = frameStyle ? frameStyle : {};
        if (this.props.width === 'sm')
            frameStyle.width = '100px';
        else if (this.props.width === 'xs')
            frameStyle.width = '70px';

        return (
            <div className="layui-input-inline" style={frameStyle}>
                <input type="checkbox" {...others} ref="thisbox" lay-filter={this.layuiFilter}/>
            </div>
        )
    }
}

/**
 * 开关
 */
class Switch extends React.Component {
    constructor(props) {
        super(props);
        this.layuiFilter = guid();
        this.stateChecked = this.props.checked;
    }

    componentDidMount() {

        if (form) {
            form.on('switch(' + this.layuiFilter + ')', (data) => this.onClick(data));
            form.render('checkbox');
        }
        else {
            let on = {"on": ['switch(' + this.layuiFilter + ')', (data) => this.onClick(data)]};
            formCalls.push(on);
        }

    }

    get checked() {
        return this.stateChecked;
    }

    set checked(value) {
        this.stateChecked = value;
        this.refs.thisSwitch.checked = this.stateChecked;
        if (form) {
            form.render('checkbox');
        }
    }

    onClick(data) {
        this.stateChecked = data.elem.checked;
        if (this.props.onClick) {
            this.props.onClick(this.stateChecked);
            form.render('checkbox');
        }
    }

    render() {
        let {frameStyle, ...others} = this.props;
        return (
            <div className="layui-input-inline" style={this.props.frameStyle}>
                <input type="checkbox" lay-skin="switch" {...others} ref="thisSwitch" lay-filter={this.layuiFilter}/>
            </div>
        )
    }
}

class Radio extends React.Component {

    constructor(props) {
        super(props);
        this.layuiFilter = guid();
        this.stateChecked = this.props.checked;
    }

    componentDidMount() {

        if (form) {
            form.on('radio(' + this.layuiFilter + ')', (data) => this.onClick(data));
            form.render('radio');
        }
        else {
            let on = {"on": ['radio(' + this.layuiFilter + ')', (data) => this.onClick(data)]};
            formCalls.push(on);
        }

    }

    get checked() {
        return this.stateChecked;
    }

    set checked(value) {
        this.stateChecked = value;
        this.refs.thisRadio.checked = this.stateChecked;
        if (form) {
            form.render('radio');
        }
    }

    onClick(data) {
        this.stateChecked = data.elem.checked;
        if (this.props.onClick) {
            this.props.onClick(this.stateChecked);
            form.render('radio');
        }
    }

    render() {
        return (
            <input type="radio" {...this.props} ref="thisRadio" lay-filter={this.layuiFilter}/>
        )
    }
}

/**
 * layui 按钮封装
 *
 * props:
 * @formAlign 假如是表单下面的确认按钮，加上这个属性可以自动对其上面的控件
 *
 * 属性都可透传, 只是封装了lay-submit
 *
 * @lay-submit  ="true"时, 会自动通过layui拦截，执行所有verify事件,ps: 不能使用{true}，只能用"true"
 *
 * 表单提交按钮样式:
 * 表单提交按钮新增了新的样式layui-form-btn, 按钮必须放在Item组件中, 依赖于global.css
 *
 * @size : sm , xs两种，中和小
 * @btnType : danger , normal, primary 三种
 * @formBtn : true 则会根据左移与layui form对齐
 */
class Button extends React.Component {
    constructor(props) {
        super(props);
        this.laySubmit = this.props['lay-submit'];
        if (this.laySubmit)
            this.layFilter = guid();
    }

    componentDidMount() {
        if (this.laySubmit) {
            if (form) {
                form.on('submit(' + this.layFilter + ')', () => this.onClick());
            }
            else {
                let on = {"on": ['submit(' + this.layFilter + ')', () => this.onClick()]};
                formCalls.push(on);
            }
        }
    }

    onClick() {
        if (this.props.onClick)
            this.props.onClick();
        return false;
    }

    render() {
        let {onClick, type, className, ...other} = this.props;
        if (this.laySubmit) {
            type = 'submit';
            other['lay-filter'] = this.layFilter;
        } else {
            type = 'button';
            other['onClick'] = this.props.onClick;
        }

        className += " layui-btn";
        if (this.props.size)
            className += " layui-btn-" + this.props.size;

        if (this.props.btnType)
            className += " layui-btn-" + this.props.btnType;

        if (this.props.formBtn)
            className += " layui-form-btn"

        return <button type={type} {...other} className={className}>{this.props.children}</button>
    }
}

/**
 * layui 静态table的样式封装，内置layui分页控件
 * ----------------------------------------------------------------
 *                 interface
 * ----------------------------------------------------------------
 * refresh() : 刷新自身
 *
 * props:
 * ----------------------------------------------------------------
 *                 分页控件
 * ----------------------------------------------------------------
 * @count 数据总条目,分页控件使用
 * @pageOnClick 分页控件被点击时的回调, 他的接口定义为
 *          function xxx(page,pageSize);
 *          参数传入页码和每页数据条目, 接口要负责从服务端拉取该范围数据并重新渲染表格
 * @pageData 用于checkbox的每页所有数据
 *
 * @noLimit : 分页控件去掉选择条数的组件，因为在layer中使用table时会撑破
 * @toolbar 表格左下方的工具按钮 ex. [<Button></Button>,<Button></Button>]
 * @wordWrap : breakWord - 表示换行，hidden - 表示不换行, 显示省略号
 *
 * ----------------------------------------------------------------
 *                 Tree Table 功能
 * ----------------------------------------------------------------
 * @expandable = true, 引入jquery插件treetable
 *     依赖样式和js:
 *     <link rel="stylesheet" href="/plugins/jquery-treeTables/css/jquery.treetable.css">
 <link rel="stylesheet" href="/plugins/jquery-treeTables/css/jquery.treetable.theme.default.css">
 <script src="/plugins/jquery-treeTables/jquery.treetable.js"></script>

 使用:
 *     tr标签上加上data-tt-id 和 data-tt-parent-id,表示自己的id和父标签id,ex:
 *     <tr data-tt-id={menu.menuCode} data-tt-parent-id={menu.menuPar}>
 *
 * 注意！！ : treeTable 由于使用到了第三方jquery插件,在tree table数据变化了需要setState自动动态渲染时，必须要调用removeTreeTable先清空一遍DOM, 再正常渲染！
 * 否则请使用接口手动渲染节点!
 * ----------------------------------------------------------------
 *                 checkbox 功能
 * ----------------------------------------------------------------
 * @checkboxRenderCallback : 用于table内的chexkbox渲染，只有需要用checkAll这个功能时才需要，它固定写为 checkboxRenderCallback={()=>{this.setState({})}} 就行
 * @checkboxKeyname 用于指定数据的key是哪个字段
 *
 * Table内的checkbox组件必须使用TCheckbox子组件
 *
 * 相关属性:
 * checkboxValue : 当前已经选中的data数组,可以设置也可以读取
 */
class Table extends React.Component {

    constructor(props) {
        super(props);
        this.laypageOption = null;
        this.page = 1;
        this.pageSize = 10;
        this.id = guid();
        this.tableId = guid();

        this.checkBoxPageState = {};
        this.checkboxSelectedData = {};
        this.keyName = this.props.checkboxKeyname;
        this.forceTreeTable = true;

        this.laypageLayout = ['prev', 'page', 'next', 'skip', 'count'];
        if (!this.props.noLimit)
            this.laypageLayout.push('limit');
    }

    componentDidMount() {
        layui.use(['laypage'], () => {
            if (this.laypageOption) {
                layui.laypage.render(this.laypageOption);
            }
        });
    }

    componentDidUpdate() {
        if (this.props.count && this.props.count > this.pageSize) {
            $("#" + this.id).show();
            this.laypageOption = {
                elem: this.id
                , count: Math.ceil(this.props.count ? this.props.count : 0)
                , layout: this.laypageLayout
                , groups: 3
                , limit: this.pageSize
                , limits: [10, 15, 20]
                , jump: (row, first) => {
                    if (!first) {
                        this.page = row.curr;
                        this.pageSize = row.limit;
                        if (this.props.pageOnClick) {
                            this.props.pageOnClick(this.page, this.pageSize);
                        }
                    }
                }
            };

            if (layui.laypage && this.props.count != this.lastCount) {
                layui.laypage.render(this.laypageOption);
                this.lastCount = this.props.count;
            }
        }
        else {
            $("#" + this.id).hide();
            if (this.props.expandable) {
                if (this.forceTreeTable) {
                    $('#' + this.tableId).treetable({
                        expandable: true,
                        onNodeCollapse: () => this.onNodeCollapse(),
                        onNodeExpand: () => this.onNodeExpand()
                    }, true);
                    this.forceTreeTable = false;
                }
            }
        }
    }

    /********* tree table 相关接口 ************
     由于jquery的插件无法使用react state更新方式来更新
     只能调用接口来完成了
     */

    /**
     * 移除某个id的节点
     * @param id
     */
    removeTreeTableNode(id) {
        if (this.props.expandable) {
            $('#' + this.tableId).treetable("removeNode", id);
        }
    }

    /**
     * 批量移除节点,在需要刷新treeTable之前必须要调用
     * @param nodes
     *
     * 使用clearTreeTable来清空DOM状态
     * 使用forceTreeTable来控制是否需要调用jquery方法
     */
    removeTreeTable() {
        this.clearTreeTable = true;
        this.forceTreeTable = true;
        this.setState({}, () => {
            this.clearTreeTable = false;
            this.forceTreeTable = true;
        });
    }

    onNodeCollapse() {
        console.log("wahaha");
    }

    onNodeExpand() {
        console.log($('#' + this.tableId).parent().height());
    }

    collapseAll() {
        $('#' + this.tableId).treetable("collapseAll");
    }

    expandAll() {
        $('#' + this.tableId).treetable("expandAll");
    }

    /******************************************/

    /**
     * 刷新当前页
     */
    refresh() {
        if (this.props.pageOnClick)
            this.props.pageOnClick(this.page, this.pageSize);
    }

    getToolBox() {
        if (this.props.toolbar) {
            return (
                <div className="toolbox-bottom">
                    {this.props.toolbar}
                </div>
            )
        }
    }

    get currPage() {
        return this.page;
    }

    /* checkbox 相关接口 */
    checkboxSelected(checked, data) {
        if (checked)
            this.checkboxSelectedData[data[this.keyName]] = data;
        else
            delete this.checkboxSelectedData[data[this.keyName]];

    }

    getCheckBoxSelectedData(key) {
        return this.checkboxSelectedData[key];
    }

    get keyname() {
        return this.keyName;
    }

    checkAll(checked) {
        this.checkBoxPageState[this.page] = checked;

        for (let i = 0; i < this.props.pageData.length; i++) {
            if (checked)
                this.checkboxSelectedData[this.props.pageData[i][this.keyName]] = this.props.pageData[i];
            else
                delete this.checkboxSelectedData[this.props.pageData[i][this.keyName]];
        }

        if (this.props.checkboxRenderCallback)
            this.props.checkboxRenderCallback();
    }

    getCheckAllState() {
        return this.checkBoxPageState[this.page];
    }

    set checkboxValue(data) {
        this.checkboxSelectedData = {};
        for (let i = 0; i < data.length; i++) {
            this.checkboxSelectedData[data[i][this.keyName]] = data[i];
        }
    }

    get checkboxValue() {
        let datas = [];
        for (let key in this.checkboxSelectedData) {
            datas.push(this.checkboxSelectedData[key]);
        }
        return datas;
    }

    /******************/

    render() {
        let sizeClass = "layui-table-responsive ";
        if (this.props.size == 'sm')
            sizeClass += "layui-table-sm";
        else if (this.props.size == 'xs')
            sizeClass += "layui-table-xs";

        let {style, frameStyle, ...others} = this.props;
        style = style ? style : {};
        if (this.props.wordWrap == 'breakWord') {
            style.whiteSpace = 'normal';
            style.wordWrap = 'break-word';
        } else if (this.props.wordWrap == 'hidden') {
            style.whiteSpace = 'nowrap';
            style.tableLayout = 'fixed';
            style.textOverflow = 'ellipsis';
            style.overflow = 'hidden';
        }
        return (
            <div style={frameStyle}>
                <div className={sizeClass}>
                    <table className="layui-table" {...this.props} id={this.tableId} style={style}>
                        {this.clearTreeTable ? '' : this.props.children}
                    </table>
                </div>
                <div id={this.id} style={{float: "right"}} className="page"></div>
                {
                    this.getToolBox()
                }
                <div style={{clear: "both"}}></div>
            </div>
        )
    }
}

/**
 * Table里面的Checkbox,用于表格项的多选控制
 *
 * props
 * @checkall : true 则表示该checkbox是顶层那个全选按钮
 * @data :    改行表格的数据,checkALl的不需要
 * @tableRef : 该checkbox所属的table的react对象引用,通过this.refs.xxx获取到
 */
class TCheckbox extends React.Component {

    constructor(props) {
        super(props);
    }

    onClick(checked) {
        if (this.checkAll) {
            this.props.tableRef.checkAll(checked);
        } else {
            this.props.tableRef.checkboxSelected(checked, this.props.data);
        }

        if (this.props.onClick)
            this.props.onClick(checked);
    }

    getCheckBox() {
        if (this.props.data && this.props.tableRef) {
            if (this.props.tableRef.getCheckBoxSelectedData(this.props.data[this.props.tableRef.keyname]))
                return <Checkbox onClick={(data) => this.onClick(data)} checked={true} lay-skin="primary"></Checkbox>
            else
                return <Checkbox onClick={(data) => this.onClick(data)} checked={false} lay-skin="primary"></Checkbox>
        } else if (this.checkAll) {
            if (this.props.tableRef.getCheckAllState())
                return <Checkbox onClick={(data) => this.onClick(data)} checked={true} lay-skin="primary"></Checkbox>
            else
                return <Checkbox onClick={(data) => this.onClick(data)} checked={false} lay-skin="primary"></Checkbox>
        } else
            return ''
    }

    render() {
        if (this.props.checkall && this.props.tableRef)
            this.checkAll = true;
        return (
            this.getCheckBox()
        )
    }
}

/**
 * layui 数据table封装，由于官网不兼容外部分页设置，所以暂无法使用
 */
class DataTable extends React.Component {

    constructor(props) {
        super(props);
        this.id = guid();
        this.tableCalls = [];
    }

    componentDidMount() {
        layui.use(['table'], () => {
            for (let i in this.tableCalls)
                for (let key in this.tableCalls[i])
                    layui.table[key](...this.tableCalls[i][key]);
        });
    }

    componentDidUpdate() {
        let ths = $("#" + this.id + " th");
        for (let i = 0; i < ths.length; i++)
            ths.attr("lay-data", "{field:'" + guid() + "'}");

        if (layui.table) {
            layui.table.init(this.id, {page: {limit: 2, count: 10}});
            layui.table.render();
        }
        else {
            this.tableCalls.push({"init": [this.id, {page: {limit: 2, count: 10}}]});
            this.tableCalls.push({"render": []});
        }
    }

    render() {
        return (
            <table className="layui-table" {...this.props} lay-filter={this.id} id={this.id}>
                {this.props.children}
            </table>
        )
    }
}

/**
 * layui Tab封装
 *
 * props:
 * @type tab的类型, 看layui 文档，layui-tab-后面的部分,brief或者不加
 * 里面的item可以使用下面的TabItem组件
 *
 * @titles 标题，类型为数组
 * @ids  每个tab的一个id标识，在调用changeTab主动切换时用到,顺序与title对应，类型数组
 *
 * @onChange(index) tab在选项卡切换时的回调,参数为index
 */
class Tab extends React.Component {

    constructor(props) {
        super(props);
        this.layFilter = guid();
    }

    componentDidMount() {
        layui.use(['element'], () => {
            layui.element.on('tab(' + this.layFilter + ')', (data) => {
                if (this.props.onChange)
                    this.props.onChange(data.index);
            })
        });
    }

    /**
     * 主动切换到某个选项卡
     * @param index
     */
    changeTab(index) {
        layui.element.tabChange(this.layFilter, index);
    }

    render() {
        return (
            <div className={"layui-tab layui-tab-" + this.props.type} lay-filter={this.layFilter}>
                <ul className="layui-tab-title">
                    {
                        this.props.titles.map((title, index) => {
                            return <li className={index == 0 ? "layui-this" : ''}
                                       lay-id={this.props.ids ? this.props.ids[index] : index}>{title}</li>
                        })
                    }
                </ul>
                <div className="layui-tab-content">
                    {this.props.children}
                </div>
            </div>
        )
    }
}

/**
 * Layui Tab里面的Item封装
 *
 * props:
 *
 * @show  默认第一次要显示哪个Tab的内容必须要加上show=true
 */
class TabItem extends React.Component {

    render() {
        if (this.props.show)
            return (
                <div className={"layui-tab-item layui-show"}>
                    {this.props.children}
                </div>
            )
        else
            return (
                <div className={"layui-tab-item"}>
                    {this.props.children}
                </div>
            )
    }
}

/**
 * 带自定义弹窗,包含了Layui Input页面和后面的一个搜索按钮
 * 点击会在下面弹出一个Modal, Modal可以包含在组件内，
 *
 * props
 *
 * @size : sm, xs, 定制里面的button的大小，input的大小只能跟随form size
 *
 * @onClick : 点击回调
 *
 * @icon : 定制后面按钮的图标，假如没有定义，则可以通过children传入文字
 *
 * inputRef : 返回input的DOM引用， 专给TreeModal使用
 *
 * ref.value : 返回当前input的值
 */
class ButtonInput extends React.Component {
    constructor(props) {
        super(props);
    }

    close() {
        this.refs.thisModal.close();
    }

    onClick() {
        if (this.props.onClick) {
            this.props.onClick();
            return;
        }
    }

    set value(value) {
        this.onChange(value);
        this.refs.input.value = value;
    }

    get value() {
        return this.refs.input.value;
    }

    get inputRef() {
        return this.refs.input.refs.input;
    }

    onChange(value) {
        if (this.props.onChange)
            this.props.onChange(value);
    }

    getButtonContent() {
        if (this.props.icon)
            return <i className="layui-icon">{this.props.icon ? this.props.icon : <span>&#xe615;</span>}</i>
        else if (this.props.children)
            return this.props.children;
        else
            return <i className="layui-icon">&#xe615;</i>
    }

    render() {
        if (this.props.frameStyle) {
            this.props.frameStyle.marginRight = 0;
        }

        let {frameStyle, onClick, children, ...others} = this.props;
        return (
            [
                <Input ref="input" onChange={this.props.onChange}
                       frameStyle={this.props.frameStyle ? this.props.frameStyle : {marginRight: 0}} {...others}/>,
                <div className="layui-word-aux" style={{
                    marginLeft: '-6px',
                    float: 'left',
                    display: 'block'
                }}>
                    <Button onClick={() => this.onClick()} size={this.props.size}
                            btnType="primary" style={{
                        border: '1px solid #E6E6E6'
                    }}>{this.getButtonContent()}</Button>
                </div>,
            ]
        )
    }
}

/**
 * tree的弹出组件，方便弹出使用
 *
 * @checkbox : true表示开启复选框
 * @onSelected(node) : 选项被点击时的回调,会传出点中的那个item的数据结构, 不管是复选框点击还是item点击,都是这个回调
 * @errorStr : 当没有任何数据的时候，显示的错误提示文字
 *
 * value : 见Tree.value
 * selectedstr : 见Tree.selectedstr
 *
 *
 */
class TreeModal extends React.Component {

    constructor(props) {
        super(props);
        this.id = guid();
    }

    get value() {
        return this.refs.tree ? this.refs.tree.value : {};
    }

    set value(value) {
        if (this.refs.tree) {
            this.refs.tree.value = value;
        }
    }

    get selectedstr() {
        return this.refs.tree ? this.refs.tree.selectedstr : {};
    }

    get modalValue() {
        return this.refs.modal.value;
    }

    /**
     * 传入具有继承关系的任意列表，构造出符合layui tree数据结构的对象数组
     * @param list 数据数组
     * @param parent 父对象名,第一次调用一定要传入null
     * @param parentIdName 存储父ID的属性名,反射使用
     * @param idName  存储本条数据ID的属性名
     * @param nameProperty 由于tree的json中需要一个属性name表示每条的显示内容
     * @returns {Array}
     */
    getNodes(list, parent, parentIdName, idName, nameProperty) {
        let node = [];
        for (let i = 0; i < list.length; i++) {
            if (!list[i][parentIdName])
                list[i][parentIdName] = null;
            if (list[i][parentIdName] == parent) {
                let subNode = this.getNodes(list, list[i][idName], parentIdName, idName, nameProperty);
                let copyListItem = Object.create(list[i]);
                copyListItem.name = list[i][nameProperty];
                if (subNode.length > 0) {
                    copyListItem.children = subNode;

                }
                node.push(copyListItem);
            }
        }

        return node;
    }

    show(value) {
        if (this.props.nodes && this.props.nodes.length > 0)
            this.refs.modal.show(value);
        else if (this.props.errorStr)
            this.refs.modal.toast(this.props.errorStr);
        else
            this.refs.modal.toast("暂无任何数据");
    }

    close() {
        this.refs.modal.close();
    }

    onSelected(data) {
        if (!this.props.checkbox) {
            this.refs.modal.close();
        }

        if (this.props.onSelected)
            this.props.onSelected(this.selectedstr, data);
    }

    render() {
        return (
            <Modal ref="modal" modalType="pop" disableOverFlow="true" {...this.props}>
                <Tree ref="tree"
                      onItemSelect={(data) => this.onSelected(data)}
                      nodes={this.props.nodes}
                      checkbox={!!this.props.checkbox}
                />
            </Modal>
        )

    }
}

/**
 * layui tree组件封装
 *
 * @checkbox : true表示开启复选框
 * @onItemSelect(node) : 选项被点击时的回调,会传出点中的那个item的数据结构, 不管是复选框点击还是item点击,都是这个回调
 *
 * value : 直接返回所有被选中的item的数据结构，假如checkbox = true, 则返回所有选中的数组, 否则, 返回点击的item数据
 *
 * selectedstr : 返回点击的item 的name 字符串，假如checkbox = true, 则返回所有选中item的name , 以','隔开
 */
class Tree extends React.Component {
    constructor(props) {
        super(props);
        this.id = guid();
        this.selectedstr = "";
    }

    updateTrees() {
        $("#" + this.id).empty();
        let options = {
            elem: '#' + this.id
            , nodes: this.props.nodes
            , check: this.props.checkbox ? 'checkbox' : false
            , onCheck: (node) => {
                this.selectedstr = "";
                if (this.props.checkbox && this.props.onItemSelect)
                    for (let i = 0; i < node.length; i++) {
                        this.selectedstr += node[i].name;
                        if (i != this.treeValue.length - 1)
                            this.selectedstr += ",";
                    }
                this.props.onItemSelect(node);
            }
            , click: (node) => {
                if (!this.props.checkbox && this.props.onItemSelect) {
                    this.selectedstr = node.name;
                    this.singleValue = node;
                    this.props.onItemSelect(node);
                }
            }
        }
        this.tree = layui.tree(options);
    }

    /**
     * 传入具有继承关系的任意列表，构造出符合layui tree数据结构的对象数组
     * @param list 数据数组
     * @param parent 父对象名,第一次调用一定要传入null
     * @param parentIdName 存储父ID的属性名,反射使用
     * @param idName  存储本条数据ID的属性名
     * @param nameProperty 由于tree的json中需要一个属性name表示每条的显示内容
     * @returns {Array}
     */
    getNodes(list, parent, parentIdName, idName, nameProperty) {
        let node = [];
        for (let i = 0; i < list.length; i++) {
            if (!list[i][parentIdName])
                list[i][parentIdName] = null;
            if (list[i][parentIdName] == parent) {
                let subNode = this.getNodes(list, list[i][idName], parentIdName, idName, nameProperty);
                let copyListItem = Object.create(list[i]);
                copyListItem.name = list[i][nameProperty];
                if (subNode.length > 0) {
                    copyListItem.children = subNode;

                }
                node.push(copyListItem);
            }
        }

        return node;
    }

    get selectedString() {
        return this.selectedstr;
    }

    get value() {
        if (this.props.checkbox)
            return this.tree.getChecked();
        else {
            return this.singleValue;
        }
    }

    set value(value) {
        if (!this.props.checkbox)
            this.singleValue = value;
    }

    componentDidMount() {
        layui.use(['tree'], () => {
            this.updateTrees();
        });
    }

    componentDidUpdate() {
        if (layui.tree)
            this.updateTrees();
    }

    render() {
        return <div id={this.id}></div>
    }

}

/**
 * layui layer 封装
 *
 * ex:
 *  <Model title="示例" ref="yyy">
 *      <xxx></xxx>
 *  </Model>
 *
 *  调用this.refs.yyy.show()显示,不需要处理关闭
 *  show(data) - data是一个对象，它的字段为
 *     {
 *         title: xxx,     显示时动态改变标题(选填)
 *         typeData : xxx, 不同的弹窗类型所携带的专有数据 (modalType为iframe, pop时必带)
 *         value : xxx     临时数据传送, 可以通过modal.value拿到(选填)
 *     }
 *
 *  toast(data) : 普通msg提示框
 *
 *  props:
 *  @area 大小尺寸数组,不填为auto, ex ['100','200']宽高
 *
 *  @disableOverFlow 禁止overflow,可以用在表格上，防止撑开
 *  value : 在使用show()方法时可以传入参数储存在value中，然后窗口处理方法中再获取
 *
 *  @modalType : 弹窗类型
 *              1. 不传，默认类型, show(data)中data可以为空
 *              2. iframe , iframe弹窗类型, 此时show(data)中data.typeData要携带跳转页面地址
 *              3. pop, 下拉弹窗类型, 此时show(data)中data.typeData要携带 DOM组件所在的ref, 也就是说，想要在input组件下方弹出,就要传入<input ref=thisInput/>这个this.refs.thisInput
 *              4. photo, 图片类型, 直接显示一张网络图片, 地址从show({typeData:xxx})传入
 *
 *  @endCallback : 窗口被关闭时的回调方法
 */
class Modal extends React.Component {

    constructor(props) {
        super(props);
        this.id = guid();
    }

    componentDidMount() {
        layui.use(['layer']);
    }

    componentDidUpdate() {
        /* 假如area配置为自适应高度, 则每次渲染完自动再次调整高度 */
        let areaHeightAuto = this.area && this.area.length == 2 ? false : true;
        let areaHeigth = $("#" + this.id).height();
        let areaParentHeight = $("#" + this.id).parent().height();
        let areaWidth = $("#" + this.id).width();
        if (this.layerIndex && !this.props.disableOverFlow && areaHeightAuto && (areaParentHeight < areaHeigth + 20 || areaParentHeight > areaHeigth + 50)) {
            $("#" + this.id).parent().height(areaHeigth + 20);
        }

        /* 下拉弹窗时,自适应宽度 */
        if (this.props.modalType == 'pop') {
            $("#" + this.id).css({minWidth: this.offsetWidth - 20});
        }
    }

    /**
     * 普通msg提示框
     * @param data
     */
    toast(data) {
        layui.layer.msg(data);
    }

    show(data) {
        let type = 1;
        let content = $("#" + this.id);
        let success = null;

        this.offset = this.props.offset ? this.props.offset : '100px';
        this.area = this.props.area ? this.props.area : ['auto', 'auto'];
        this.shade = this.props.shade ? this.props.shade : 0.3;
        this.skin = this.props.skin ? this.props.skin : '';

        if (this.props.modalType == "iframe") {
            type = 2;
            content = data.typeData;
            success = (layero, index) => {
                layui.layer.iframeAuto(index);
            }
        } else if (this.props.modalType == "pop") {
            const input = ReactDOM.findDOMNode(data.typeData);
            this.offsetHeight = data.typeData.offsetHeight;
            this.offsetWidth = data.typeData.offsetWidth;
            let offset = $(input).offset();
            let scrollTop = $(window).scrollTop();
            this.offset = [offset.top - scrollTop + this.offsetHeight + 10, offset.left];
            this.area = ['auto', '300px'];
            this.shade = 0.0001;
            this.skin = 'layui-layer-border';
            this.title = false;
            this.componentDidUpdate();
        } else if (this.props.modalType == "photo") {
            layui.layer.photos({
                photos: {
                    "title": "图片",
                    "data": [
                        {
                            "src": data.typeData
                        }
                    ]
                }, anim: 5
            });
            return;
        }
        else {
            this.title = (data && data.title) ? data.title : this.props.title;
        }

        let layerObj = {
            type: type,
            closeBtn: 0, //不显示关闭按钮
            shadeClose: true, //开启遮罩关闭
            fix: false,
            shade: this.shade,
            offset: this.offset,
            area: this.area,
            skin: this.skin,
            title: this.title,
            content: content,
            success: success,
            end: () => {
                this.cancel()
            }
        };

        this.layerIndex = layui.layer.open(layerObj);

        this.savevalue = data ? data.value : null;
    }

    get value() {
        return this.savevalue;
    }

    set value(value) {
        this.savevalue = value;
    }

    cancel() {
        this.layerIndex = null;
        if (this.props.endCallback)
            this.props.endCallback();
    }

    close() {
        layui.layer.close(this.layerIndex);
        this.layerIndex = null;
    }

    render() {
        let className = "layui-layer-overflow";
        if (this.props.disableOverFlow) {
            if (this.props.modalType === "pop")
                className = "layui-layer-over-flow-disable-pop";
            else
                className = "layui-layer-over-flow-disable";
        }
        return (
            <div className={className}>
                <div id={this.id} style={{display: "none", margin: 10}}>
                    {this.props.children}
                </div>
            </div>
        )
    }
}

class Tips extends React.Component {
    constructor(props) {
        super(props);
        this.id = guid();
        this.directionMap = {
            up: 1,
            right: 2,
            down: 3,
            left: 4
        }

        this.time = this.props.time ? this.props.time : 1000000;
    }

    componentDidMount() {
        layui.use(['layer']);
        this.tips = [];
        if (this.props.direction) {
            this.tips.push(this.directionMap[this.props.direction]);
            if (this.props.color)
                this.tips.push(this.props.color);
            else
                this.tips.push('#403F3E');
        } else {
            this.tips = [1, '#403F3E'];
        }
    }

    onMouseOver() {
        this.layerIndex = layui.layer.tips(this.props.children, '#' + this.id, {tips: this.tips, time: this.time});
    }

    onMouseLeave() {
        layui.layer.close(this.layerIndex);
    }

    render() {
        return (
            <i className="layui-icon layui-react-tips" {...this.props} onMouseOver={() => this.onMouseOver()}
               onMouseLeave={() => this.onMouseLeave()} id={this.id}>&#xe60b;</i>
        )
    }
}

/**
 * Layui laydate控件封装
 *
 * props:
 *
 * @type: 默认值 date
 *      year    年选择器    只提供年列表选择
 month    年月选择器    只提供年、月选择
 date    日期选择器    可选择：年、月、日。type默认值，一般可不填
 time    时间选择器    只提供时、分、秒选择
 datetime    日期时间选择器    可选择：年、月、日、时、分、秒

 @range: 非空则开启左右面板范围选择

 @value: 初始值
 @rangeStart : 假如设置了range, 这个返回range起始值
 @rangeEnd : 假如设置了range, 这个返回range结束值

 @min : 时间范围初始值
 @max : 时间范围最大值

 @done: function(value, date, endDate), 选择时间后的回调,回调返回三个参数，分别代表：生成的值、日期时间对象、结束的日期时间对象

 @showBottom : 'false'禁止底部按钮栏

 @calendar : 日期是否显示阴历和节日
 */

class DateSelect extends React.Component {

    constructor(props) {
        super(props);
        this.id = guid();
    }

    componentDidMount() {
        let range = this.props.range ? "~" : false;
        let renderObj = {
            range: range,
            elem: '#' + this.id,
            value: this.props.value,
            calendar: !!this.props.calendar
        }
        if (this.props.type)
            renderObj.type = this.props.type;
        if (this.props.min)
            renderObj.min = this.props.min;
        if (this.props.max)
            renderObj.max = this.props.max;
        if (this.props.done)
            renderObj.done = this.props.done;
        if (this.props.showBottom == 'false')
            renderObj.showBottom = false;

        layui.use(['laydate'], () => {
            layui.laydate.render(renderObj);
        });
    }

    get value() {
        return this.refs.input.value;
    }

    set value(value) {
        this.refs.input.value = value;
    }

    get rangeStart() {
        if (!this.props.range)
            return "-1";
        let range = this.refs.input.value;
        let values = range.split(" ~ ");
        return values[0];
    }

    get rangeEnd() {
        if (!this.props.range)
            return "-1";
        let range = this.refs.input.value;
        let values = range.split(" ~ ");
        return values[1];
    }

    render() {
        return (
            <Input id={this.id} {...this.props} ref="input" readonly={true}/>
        )
    }
}

/**
 * layui的提示信息
 */
class Blockquote extends React.Component {

    render() {
        let style = this.props.style ? this.props.style : {};
        if (this.props.size === "sm")
            style.padding = "10px";
        else if (this.props.size === "xs") {
            style.padding = "5px";
        }
        return (
            <blockquote className="layui-elem-quote" style={style}>{this.props.children}</blockquote>
        )
    }
}

/**
 * layui 卡片面板
 */
class Card extends React.Component {
    render() {

        return (
            <div className="layui-card">
                <div className="layui-card-header">{this.props.header}</div>
                <div className="layui-card-body">
                    {this.props.children}
                </div>
            </div>
        )
    }
}

/**
 * layui 栅格布局
 */
class Row extends React.Component {
    constructor(props) {
        super(props);

        this.rowClass = "layui-row";

        if (this.props.space)
            this.rowClass += " layui-col-space" + this.props.space;

    }

    render() {
        return (
            <div className={this.rowClass} {...this.props}>
                {this.props.children}
            </div>
        )
    }
}

/**
 * layui 栅格列
 *
 * @xs,sm,md,lg   ex. xs=6, md=6
 * @xs-offset, sm-offset,md-offset,lg-offset
 */
class Col extends React.Component {
    constructor(props) {
        super(props);
        this.colType = ['xs', 'sm', 'md', 'lg'];
        this.colClass = "";
        for (let i = 0; i < this.colType.length; i++) {
            if (this.props[this.colType[i]])
                this.colClass += " layui-col-" + this.colType[i] + this.props[this.colType[i]];

            if (this.props[this.colType[i] + "-offset"])
                this.colClass += " layui-col-" + this.colType[i] + "-offset" + this.props[this.colType[i] + "-offset"];
        }

    }

    render() {
        return (
            <div className={this.colClass} style={this.props.style}>
                {this.props.children}
            </div>
        )
    }
}

/**
 * layui进度条
 */
class ProgressBar extends React.Component {
    constructor(props) {
        super(props);
        this.style = {
            title: {
                position: "absolute",
                right: 0,
                top: -35,
                fontSize: 14,
                color: "#999"
            },
            percent: {
                position: "relative",
                top: -10,
                lineHeight: "26px",
                fontSize: 26,
                color: "#666"
            }
        }
    }

    componentDidMount() {
        layui.use(['element'], () => {
            layui.element.render("progress");
        })
    }

    componentDidUpdate() {
        if (layui.element)
            layui.element.render("progress");
    }

    render() {
        this.progressBarClass = "layui-progress-bar";
        if (this.props.color)
            this.progressBarClass += " layui-bg-" + this.props.color;
        else {
            let percent = parseInt(this.props.percent.substr(0, this.props.percent.length - 1))
            if (percent > 50)
                this.progressBarClass += " layui-bg-green";
            else if (percent > 25)
                this.progressBarClass += " layui-bg-orange";
            else
                this.progressBarClass += " layui-bg-red";
        }


        return (
            <div style={{margin: "20px 0 10px"}}>
                <span style={this.style.percent}>{this.props.percent}</span>
                <div className="layui-progress">
                    <h3 style={this.style.title}>{this.props.title}</h3>
                    <div className={this.progressBarClass} lay-percent={this.props.percent}></div>
                </div>
            </div>
        )
    }
}